﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.Remoting.Messaging;
using System.Collections;
using Summer.Core.Config;

#region License
/*
 *  Copyright (c) Dumuzi.cn 2009 , All rights reserved.
 *  CLR Version:  2.0.50727.4927
 *  NameSpace: Summer.Core.Aop
 *  Author:Edwin Tai,戴佳顺
 *  Msn:edwin19861218@hotmail.com
 *  QQ: 1961218
 *  Date:2009/10/27 16:37:20
 *  Licensed under the Microsoft Public License (Ms-PL)
 *  This license governs use of the accompanying software. 
 *  If you use the software, you accept this license.
 *  If you do not accept the license, do not use the software.
 */
#endregion

namespace Summer.Core.Aop
{
    public abstract class AbstractAspect : IMessageSink
    {
        private IMessageSink m_nextSink = null;
        private SortedList m_beforeAdvices = new SortedList();
        private SortedList m_afterAdvices = new SortedList();
        private AopConfiguration m_configuration = null;

        public AbstractAspect(IMessageSink nextSink)
        {
            m_nextSink = nextSink;
        }
        public AbstractAspect(IMessageSink nextSink, AopConfiguration config)
        {
            m_nextSink = nextSink;
            m_configuration = config;
        }
        public IMessageSink NextSink
        {
            get { return m_nextSink; }
        }
        public AopConfiguration Configuration
        {
            set { this.m_configuration = value; }
            get
            {
                if (this.m_configuration == null)
                {
                    return new AopConfiguration();
                }
                return m_configuration;
            }

        }
        public virtual IMessage SyncProcessMessage(IMessage msg) 
        {
            return SyncProcessMessage(msg, null, null);
        }
        /// <summary>
        /// Process message and run aspect function
        /// </summary>
        /// <param name="msg"></param>
        /// <returns>Answer message</returns>
        public IMessage SyncProcessMessage(IMessage msg, Action beforeRun, Action afterRun)
        {
            //Before aspect proccess
            IMethodCallMessage call = msg as IMethodCallMessage;
            string methodName = call.MethodName;
            IBeforeAdvice before = FindBeforeAdvice(methodName);
            if (before != null)
            {
                if (beforeRun != null)
                {
                    beforeRun();
                }
                before.BeforeAdvice(call);
            }
            //run
            IMessage retMsg = m_nextSink.SyncProcessMessage(msg);
            //After aspect process
            IMethodReturnMessage reply = retMsg as IMethodReturnMessage;
            IAfterAdvice after = FindAfterAdvice(methodName);
            if (after != null)
            {
                if (afterRun != null)
                {
                    afterRun();
                }
                after.AfterAdvice(reply);
            }
            return retMsg;
        }
        public IMessageCtrl AsyncProcessMessage(IMessage msg, IMessageSink replySink)
        {
            return null;
        }
        /// <summary>
        /// Input before and after aspect function
        /// </summary>
        /// <param name="aspectXml"></param>
        /// <param name="aspectName"></param>
        public void ReadAspect(string aspectXml, string aspectName)
        {
            //Get new instance of IBeforeAdvice
            IBeforeAdvice before = (IBeforeAdvice)Configuration.GetAdvice(aspectXml, aspectName, AdviceEnum.Before);
            //Get before aspect functions' name
            string[] beforeNames = Configuration.GetFunctionNames(aspectXml, aspectName, AdviceEnum.Before);
            //Input before aspect
            foreach (string name in beforeNames)
            {
                AddBeforeAdvice(name, before);
            }
            //Get new instance of IAfterAdvice
            IAfterAdvice after = (IAfterAdvice)Configuration.GetAdvice(aspectXml, aspectName, AdviceEnum.After);
            //Get after aspect functions' name
            string[] afterNames = Configuration.GetFunctionNames(aspectXml, aspectName, AdviceEnum.After);
            //Input after aspects
            foreach (string name in afterNames)
            {
                AddAfterAdvice(name, after);
            }
        }
        protected virtual void AddBeforeAdvice(string methodName, IBeforeAdvice before)
        {
            //Multithreading support
            lock (this.m_beforeAdvices)
            {
                if (!m_beforeAdvices.Contains(methodName))
                {
                    m_beforeAdvices.Add(methodName, before);
                }
            }
        }
        protected virtual void AddAfterAdvice(string methodName, IAfterAdvice after)
        {
            //Multithreading support
            lock (this.m_afterAdvices)
            {
                if (!m_afterAdvices.Contains(methodName))
                {
                    m_afterAdvices.Add(methodName, after);
                }
            }
        }
        /// <summary>
        /// Get IBeforeAdvice from tempList
        /// </summary>
        /// <param name="methodName">Upper</param>
        /// <returns></returns>
        public IBeforeAdvice FindBeforeAdvice(string methodName)
        {
            IBeforeAdvice before;
            lock (this.m_beforeAdvices)
            {
                before = (IBeforeAdvice)m_beforeAdvices[methodName];
            }
            return before;
        }
        /// <summary>
        /// Get IAfterAdvice from tempList
        /// </summary>
        /// <param name="methodName">Upper</param>
        /// <returns></returns>
        public IAfterAdvice FindAfterAdvice(string methodName)
        {
            IAfterAdvice after;
            lock (this.m_afterAdvices)
            {
                after = (IAfterAdvice)m_afterAdvices[methodName];
            }
            return after;
        }
    }


}
